 /* Copyright (C)
  * 2024 ZKLX Optoelectronic Technology Co.LTD
  * All rights reserved.
  */
 /**
  * @file main.cpp
  * @brief 
  * @author xuke
  * @version 1.0
  * @date 2024-09-28
  */

#include "/home/xuke/bin/dbg.h"
#include <iostream>
#include <thread>
#include <cJSON.h>
#include <unistd.h>
#include <crow_all.h>
#include <opencv2/opencv.hpp>
#include "misc_tool.h"

/*--------------------------------extern--------------------------------------------*/

/*--------------------------------globals-------------------------------------------*/

/*--------------------------------locals--------------------------------------------*/
static uint8_t l_ws_connected = 0;
static std::string test_ws_osd;

/*--------------------------------funcs---------------------------------------------*/
/**
 * @brief 图片发送显示测试
 * @param conn
 */
void image_sender_test(void *arg)
{
	crow::websocket::connection *conn = (crow::websocket::connection*)arg;
	std::ifstream file("./www/img/favicon.ico", std::ios::binary);

	if (file) {
		// 文件存在，读取数据
		std::string img_data = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
		while (true) {
			if (!l_ws_connected) {
				return;
			}
			conn->send_binary(img_data); // 成功读取，通过WebSocket发送
			std::this_thread::sleep_for(std::chrono::milliseconds(40)); //25HZ
		}
	} else { // 文件不存在的处理
		conn->send_text("file NOT found");
	}
}

/**
 * @brief video websocket推流线程
 * @param conn
 */
void frame_sender_sm(void *arg)
{
	crow::websocket::connection *conn = (crow::websocket::connection*)arg;
	std::vector<uchar> buf;
	cv::Mat frame;
	cv::Mat scale_frame;
	uint8_t scale_val = 2;

	cv::namedWindow("cv_show", cv::WINDOW_KEEPRATIO);
	while (true) {
		// 摄像头捕获
		cv::VideoCapture capture("/home/xuke/tftpboot/ftv_1.mp4");
		if (!capture.isOpened()) {
			std::cerr << "无法打开视频文件" << std::endl;
			return;
		}

		while (capture.read(frame)) {
			if (!l_ws_connected) {
				capture.release();
				cv::destroyAllWindows();
				return;
			}

			//frame 处理
			cv::putText(frame, test_ws_osd, cv::Point(100, 200), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 255, 255), 2, cv::LINE_AA);
			cv::resize(frame, scale_frame, cv::Size(frame.cols/scale_val, frame.rows/scale_val));

			// 编码为JPEG格式
			cv::imencode(".jpg", scale_frame, buf);

			// 发送JPEG数据给前端
			if (l_ws_connected) {
				std::string bin_string(buf.size(), 0);
				std::copy(buf.begin(), buf.end(), bin_string.begin());
				if (conn != nullptr) {
					conn->send_binary(bin_string);
				}
			}

#if 0
			//本地show对比测试
			cv::imshow("cv_show", frame);
			cv::waitKey(40); //25Hz
#else
			std::this_thread::sleep_for(std::chrono::milliseconds(40)); //25HZ
#endif
		}
	}
}

int run_web_server()
{
	crow::mustache::set_global_base("./www");
	crow::SimpleApp app;

	//默认页
	CROW_ROUTE(app, "/") ([]() {
		auto page = crow::mustache::load("index.html");
		return page.render();
	});

	// 处理HTML
	CROW_ROUTE(app, "/html/<string>") ([](const crow::request& req, std::string htmlFile) {
		std::string htmlPath = "./www/html/" + htmlFile; // HTML路径
		std::ifstream t(htmlPath);
		std::string htmlCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "text/html");
		res.body = htmlCode;
		return res;
	});

	// 处理CSS
	CROW_ROUTE(app, "/css/<string>") ([](const crow::request& req, std::string cssFile) {
		std::string cssPath = "./www/css/" + cssFile; // CSS路径
		std::ifstream t(cssPath);
		std::string cssCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "text/css");
		res.body = cssCode;
		return res;
	});

	// 处理JS
	CROW_ROUTE(app, "/js/<string>") ([](const crow::request& req, std::string jsFile) {
		std::string jsPath = "./www/js/" + jsFile; // JS路径
		std::ifstream t(jsPath);
		std::string jsCode((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());

		crow::response res;
		res.set_header("Content-Type", "application/javascript");
		res.body = jsCode;
		return res;
	});

	// 处理图片请求的路由
	CROW_ROUTE(app, "/img/<string>") ([](const crow::request& req, std::string imgFile) {
		// 打开图片文件
		std::string imgPath = "./www/img/" + imgFile; // 图片路径
		std::ifstream file(imgPath, std::ios::binary);
		if (!file) {
			return crow::response(404); // 如果文件不存在，返回404错误
		}

		// 创建响应对象
		crow::response res;
		res.set_header("Content-Type", "image/jpng"); // 设置内容类型为图片
		res.body = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // 读取文件内容到响应体
		return res;
	});

	//websocket
	CROW_WEBSOCKET_ROUTE(app, "/ws/video")
		.onopen([&](crow::websocket::connection& conn) {
			// WebSocket连接已建立
			my_debug_msg("onopen");
			if (!l_ws_connected) {
				l_ws_connected = 1;

#if 0
				// image tester for CPU (no opencv thread)
				std::thread file_cap_task_thread(image_sender_test, (void*)&conn);
				file_cap_task_thread.detach();
#else
				//player thread
				std::thread file_cap_task_thread(frame_sender_sm, (void*)&conn);
				file_cap_task_thread.detach();
#endif
				my_debug_msg("Local file frame capture task start");
			} else {
				my_debug_msg("capture task is already running!");
			}
		})
		.onclose([&](crow::websocket::connection& conn, const std::string& reason) {
			// WebSocket连接已关闭
			my_debug_msg("onclose");
			l_ws_connected = 0;
		})
		.onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary){
			// 处理从前端接收的WebSocket消息, 在这里处理音视频数据的传输
			if (is_binary) { //二进制数处理
				my_debug_msg("onmessage recv binary data.");
				//conn.send_binary(data); //echo
			} else {
				my_debug_msg("onmessage: %s", data.c_str());
				//conn.send_text(data); //echo
				test_ws_osd = data;
				
				/* ZklxTag: xuke modified on 2024/10/11 for fixing M# json解析处理 */
				cJSON *root = cJSON_Parse(data.c_str());
				if (!root) {
					fprintf(stderr, "Error json format: [%s]\n", cJSON_GetErrorPtr());
					return;
				}

				//show all
				char *root_str = cJSON_Print(root);
				my_debug_msg("root_str:[%s]", root_str);
				free(root_str);

				//get nodes
				char *cmd_type = XK_JSON_GET_STRING(root, "type");
				if (!cmd_type) {
					return;
				}
				my_debug_msg("get [type]: %s\n", cmd_type);
				if (!strcmp(cmd_type, "rect")) {
					cJSON *sub_obj = XK_JSON_GET_OBJ(root, cmd_type);
					my_debug_msg("get [rect]: [%d, %d, w%d, h%d]", XK_JSON_GET_INTEGER(sub_obj, "x"), XK_JSON_GET_INTEGER(sub_obj, "y"), XK_JSON_GET_INTEGER(sub_obj, "w"), XK_JSON_GET_INTEGER(sub_obj, "h"));
				}
				if (!strcmp(cmd_type, "point")) {
					cJSON *sub_obj = XK_JSON_GET_OBJ(root, cmd_type);
					my_debug_msg("get [point]: [%d, %d]", XK_JSON_GET_INTEGER(sub_obj, "x"), XK_JSON_GET_INTEGER(sub_obj, "y"));
				}

				//free
				cJSON_Delete(root);
			}
		});

	//其他route
	CROW_ROUTE(app, "/<string>") ([](const crow::request& req, std::string args) {
		 my_debug_msg("unsupport route for: [%s]", args.c_str());
		 return crow::response(404);
	});

	app.port(18080).multithreaded().run();
	return 0;
}

/**
 * @brief main
 * @param argc
 * @param argv[]
 * @return 0
 */
int main(int argc, const char *argv[])
{
	run_web_server();
	return 0;
}

